/**************************************************************************
 * Copyright (C) 2012 Tyler Robertson
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

#include "Arduino.h"
#include "SPI/SerialPeripheralInterface.h"
#include "Wifly/WiflyDevice.h"
#include "Input.h"

#define MINUTES_TO_MILLIS( x ) ((unsigned long)(x * 60 * 1000))


/*************** Network parameters ****************/
#define NETWORK_SSID "NegativeEntropy"
#define NETWORK_PASSPHRASE "Pluginacable!"
#define EXOSITE_CIK "48e0e7edd2ae31512575467451da10ce0b1da7ff"
#define EXOSITE_DATA_STORE "MacTest"


/*************** Timeout parameters ****************/
#define WIFLY_DELAY_AMOUNT				( 50 )
#define FAN_MOVEMENT_TIMEOUT			MINUTES_TO_MILLIS( 3.0f )
#define INTERNAL_MOTION_TRIGGER_PERIOD	MINUTES_TO_MILLIS( 10.0f )


/****************** Pin mappings ******************/
#define INT_MOTION_SENSOR_PIN	2
#define EXT_MOTION_SENSOR_PIN	7
#define ACTION_BUTTON_PIN		4
#define LOW_LIGHT_SENSOR_PIN	1
#define FAN_SWITCH_PIN			1
#define INT_LIGHT_RED_PIN		5
#define INT_LIGHT_GREEN_PIN		6
#define INT_LIGHT_BLUE_PIN		9
#define ACTIVITY_LIGHT_PIN		3

/******************** Inputs ************************/
DigitalPullupInput< INT_MOTION_SENSOR_PIN >			internalMotionSensor;
DigitalPullupInput< EXT_MOTION_SENSOR_PIN >			externalMotionSensor;
DigitalPullupInput< ACTION_BUTTON_PIN >				actionButton;
AnalogInput< LOW_LIGHT_SENSOR_PIN, less_than, 25 >	lowLightSensor;

bool systemPaused = true;
unsigned char numTimesEnteredDome = 1;



//-------------------------------------------------------
void SetInternalLight( float pct ) // <-- linear space
{
	// Gamma correct that shit!
	pct = pow( pct, 2.2f );
	
	unsigned char val = (unsigned char)(pct*255.0f);
	
	analogWrite( INT_LIGHT_RED_PIN, val );
	analogWrite( INT_LIGHT_GREEN_PIN, val );
	analogWrite( INT_LIGHT_BLUE_PIN, val );
}

//-------------------------------------------------------
unsigned char GetBlinkValue( int cycleMS )
{
	int time = millis() % cycleMS;
	
	if( time > cycleMS >> 1 )
	{
		time = -time + cycleMS;
	}
	
	// Raw output
	float pct = (float(time)) / (float(cycleMS));
	
	return (unsigned char)(255.0f*pow( pct, 2.2f ));
}

//-------------------------------------------------------
void ProcessActivityLight()
{
	if( systemPaused )
	{
		// Fast blink
		analogWrite( ACTIVITY_LIGHT_PIN, GetBlinkValue( 250 ) );
	}
	else if( numTimesEnteredDome > 0 )
	{
		// Slow blink
		analogWrite( ACTIVITY_LIGHT_PIN, GetBlinkValue( 3000 ) );
	}
	else
	{
		// Off
		analogWrite( ACTIVITY_LIGHT_PIN, 0 );
	}
}

//-------------------------------------------------------
void SendDataToServer( const char *store, unsigned char data )
{
	char contentStr[24];
	char contentsLenStr[8];
	
	unsigned int len = sprintf( contentStr, "%s=%d", store, data );
	sprintf( contentsLenStr, "%d", len );
	
	if( wifly_open_connection( "m2.exosite.com", 80 ) )
	{		
		wifly_send_string( Crlf( "POST /api:v1/stack/alias HTTP/1.1" ) );						delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( Crlf( "Host: m2.exosite.com" ) );									delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( Crlf( "X-Exosite-CIK: " EXOSITE_CIK ) );								delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( Crlf( "Connection: Close" ) );										delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( Crlf( "Content-Type: application/x-www-form-urlencoded" ) );			delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( "Content-Length: " );												delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( contentsLenStr );													delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( "\r\n\r\n" );														delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( contentStr );														delay( WIFLY_DELAY_AMOUNT );
		wifly_send_string( "\r\n" );															
	}
	else
	{
		Serial.println( "Could not connect to server" );
	}

}

//-------------------------------------------------------
unsigned long GetTimeSinceMovementDetected()
{
	return millis() - max( externalMotionSensor.GetTimeLastActive(), internalMotionSensor.GetTimeLastActive() );
}

//-------------------------------------------------------
void ProcessMotionSensorsAndFans()
{
	if( systemPaused || GetTimeSinceMovementDetected() < FAN_MOVEMENT_TIMEOUT )
	{
		// Turn off fans
		// Turn off light?
		
		digitalWrite( FAN_SWITCH_PIN, LOW );
	}
	else
	{
		// Turn on fans
		// Turn on light?
		
		digitalWrite( FAN_SWITCH_PIN, HIGH );
	}
	
	if( !systemPaused && internalMotionSensor.DidJustActivate() &&
	   (millis() - internalMotionSensor.GetTimeLastActive()) > INTERNAL_MOTION_TRIGGER_PERIOD )
	{
		++numTimesEnteredDome;
		SendDataToServer( EXOSITE_DATA_STORE, numTimesEnteredDome );
	}
}

//-------------------------------------------------------
void ProcessActionButton()
{
	if( actionButton.DidJustDeactivate() )
	{
		systemPaused = !systemPaused;
		
		if( !systemPaused )
		{
			internalMotionSensor.Reset();
			externalMotionSensor.Reset();
			
			numTimesEnteredDome = 0;
			SendDataToServer( EXOSITE_DATA_STORE, 0 );
		}
	}
	
	if( systemPaused )
	{
		// Pulse action LED
		analogWrite( ACTIVITY_LIGHT_PIN, (unsigned char)(255*(millis() % 1000)/1000.0f));
	}
}

//-------------------------------------------------------
void setup()
{
	Serial.begin( 9600 );
	
	spi_initialize();
	wifly_initialize();

	pinMode( FAN_SWITCH_PIN, OUTPUT );
	
	pinMode( INT_LIGHT_RED_PIN, OUTPUT );
	pinMode( INT_LIGHT_GREEN_PIN, OUTPUT );
	pinMode( INT_LIGHT_BLUE_PIN, OUTPUT );
	
	internalMotionSensor.Initialize();
	externalMotionSensor.Initialize();
	actionButton.Initialize();
//	lowLightSensor.Initialize();
	
	actionButton.SetDebounceDelay( 50 );
	
	//wifly_debug_serial();
	
	//delay( 1000 );
	//wifly_enter_command_mode();
	//SendDataToServer( DATA_STORE, numTimesEnteredDome );
	
//	while( !wifly_enter_command_mode() )
//			wifly_debug_serial();
//	
//	Serial.println("Here" );
//
//	
//	while( !wifly_send_command( Crlf( "set ip dchp 1" ), "AOK" ) )		wifly_debug_serial();
//	while( !wifly_send_command( Crlf( "set wlan join 1"), "AOK"  ) 	) wifly_debug_serial();
//	while( !wifly_send_command( Crlf( "set wlan phrase Pluginacable!" ), "AOK"  ) )	wifly_debug_serial();
//	while( !wifly_send_command( Crlf( "set wlan ssid NegativeEntropy" ), "AOK"  ) )	wifly_debug_serial();
//	wifly_send_command( Crlf( "save" ) );	wifly_debug_serial();
//	wifly_send_command( Crlf( "reboot" ) ); wifly_debug_serial();
//	wifly_send_command( Crlf( "join" ) );  wifly_debug_serial();
//	wifly_send_command( Crlf( "get everything" ) );	wifly_debug_serial();
}

//-------------------------------------------------------
void loop()
{
	
//	wifly_debug_serial();
//	if( millis() - time > 10000 )
//	{
//		time = millis();
//		
//		Serial.println( "Trying to enter command mode" );
//		wifly_close_connection();
//		
//		bool ret = false;
//		do
//			ret = wifly_enter_command_mode();
//		while( !ret);
//		
//		
//		SendDataToServer( DATA_STORE, 1 );
//	}
//	return;
	
	internalMotionSensor.Update();
	externalMotionSensor.Update();
//	actionButton.Update();
//	lowLightSensor.Update();

	//if( internalMotionSensor.IsActive() )
	{
	//	Serial.println( "Internal: ");
	}
	
	//if( externalMotionSensor.IsActive() )
	{
	//	Serial.println( "External: ");
	}
	//Serial.print( "   External: ");
//	Serial.println( internalMotionSensor.GetRawValue() );
	Serial.println( externalMotionSensor.GetRawValue() );
	
	
//	if( actionButton.DidJustActivate() )
//	{
//		++numTimesEnteredDome;
//		Serial.println( "Active" );
//		SendDataToServer( DATA_STORE, numTimesEnteredDome );
//	}
	
	return;
	
	/***** Inputs *******/
//	ProcessMotionSensorsAndFans();
//	ProcessActionButton();
	
	Serial.println( internalMotionSensor.GetRawValue() );
	
	if( millis() - internalMotionSensor.GetTimeLastActive() < 5000 )
	{
//		Serial.println( "Off" );
		digitalWrite( FAN_SWITCH_PIN, LOW );
	}
	else
	{
//		Serial.println( "On");
		digitalWrite( FAN_SWITCH_PIN, 255 );
	}
	
	/****** Outputs *******/
	ProcessActivityLight();
//	analogWrite( INT_LIGHT_PIN, lowLightSensor.IsActive() ?  1 : 0 ); // Internal LED
//	analogWrite( ACTIVITY_LIGHT_PIN, numTimesEnteredDome > 0 ? 128 : 0 ); // Activity LED
}
